package com.birdy.ws.action;

import com.alibaba.fastjson.JSONObject;
import com.birdy.con.AppConst;
import com.birdy.dto.Response;
import com.birdy.exception.ServiceException;
import com.birdy.jwt.exception.InvalidTokenException;
import com.birdy.jwt.service.ITokenService;
import com.birdy.redis.RedisFactory;
import com.birdy.redis.code.RedisSettingCode;
import com.birdy.room.service.IRoomService;
import com.birdy.user.service.IUserService;
import com.birdy.vo.Room;
import com.birdy.vo.UserInfo;
import com.birdy.ws.code.MessageCode;
import com.birdy.ws.map.UserSessionMap;
import com.birdy.ws.util.SpringContext;
import io.lettuce.core.api.reactive.RedisReactiveCommands;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author birdy
 * @date 2022/4/6 3:48 PM
 */
@ServerEndpoint("/ws/player/{token}")
@Component
@Slf4j
public class PlayerAction {
    /**
     * jwt token service
     * spring管理的bean都是单例，和websocket(多对象)冲突，不能使用autowired注入。
     * @return token service
     */
    public ITokenService getTokenService() {
        return SpringContext.getBean(ITokenService.class);
    }
    public IUserService getUserService() {
        return SpringContext.getBean(IUserService.class);
    }
    public IRoomService getRoomService() {
        return SpringContext.getBean(IRoomService.class);
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token")String token) throws ServiceException {
        UserInfo userInfo = null;
        try {
            // 获取玩家的信息
            userInfo = getUserService().getUserInfo(getTokenService().getOpenid(token));
            log.info("{}", userInfo);
        } catch (Exception e) {
            log.error("{}", e.getMessage());
        }
        // 获取在线玩家列表
        List<UserInfo> userInfos = getRoomService().getUserList(UserSessionMap.getUserSessionMap());
//        List<UserInfo> userInfos = getRoomService().getUserList(sessionMap);
        if (userInfo != null) {
            joinRoom(userInfo, session);
            UserSessionMap.addUserSession(userInfo.getOpenId(), session);
//            sessionMap.put(userInfo.getOpenId(), session);
//            log.info("有新用户加入，openid={}, 当前在线人数为：{}", userInfo.getOpenId(), sessionMap.size());
            log.info("有新用户加入，openid={}, 当前在线人数为：{}", userInfo.getOpenId(),
                    UserSessionMap.getUserSessionMapSize());
            // 加入游戏者信息
            Response res = Response.builder().res(MessageCode.USER_LOGIN.getCode())
                    .msg(MessageCode.USER_LOGIN.getMessage())
                    .data(AppConst.KEY_USER_INFO, userInfo).build();
            // 在线玩家列表信息
            Response userInfosRes = Response.builder().res(MessageCode.USER_LIST.getCode())
                    .msg(MessageCode.USER_LIST.getMessage())
                    .data(AppConst.KEY_USER_LIST, JSONObject.toJSON(userInfos)).build();
            // 初始化在线玩家
            sendMessage(JSONObject.toJSONString(userInfosRes), session);
            // 给其他玩家发消息我加入了游戏
            sendAllMessage(JSONObject.toJSONString(res));  // 后台发送消息给所有的客户端
        } else {
            log.error("没有在线玩家");
            throw new ServiceException("userinfo not exist!");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose(Session session, @PathParam("token") String token) {
        try {
            UserInfo userInfo = getUserService().getUserInfo(getTokenService().getOpenid(token));
            UserSessionMap.removeSession(userInfo.getOpenId());
//            sessionMap.remove(userInfo.getOpenId());
//            log.info("有一连接关闭，移除openid={}的用户session, 当前在线人数为：{}", userInfo.getOpenId(), sessionMap.size());
            log.info("有一连接关闭，移除openid={}的用户session, 当前在线人数为：{}", userInfo.getOpenId(),
                    UserSessionMap.getUserSessionMapSize());
            Response res = Response.builder().res(MessageCode.USER_LOGOUT.getCode())
                    .msg(MessageCode.USER_LOGOUT.getMessage())
                    .data(AppConst.KEY_USER_INFO, userInfo).build();
            sendAllMessage(JSONObject.toJSONString(res));  // 后台发送消息给所有的客户端
        } catch (Exception e) {
            log.error("{}", e.getMessage());
        }
    }
    /**
     * 收到客户端消息后调用的方法
     * 后台收到客户端发送过来的消息
     * onMessage 是一个消息的中转站
     * 接受 浏览器端 socket.send 发送过来的 json数据
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session, @PathParam("token") String token) {
        log.info("服务端收到用户username={}的消息:{}", token, message);
        JSONObject jsonObject = JSONObject.parseObject(message);
        UserInfo userInfo;
        try {
            userInfo = getUserService().getUserInfo(getTokenService().getOpenid(token));
            if (userInfo == null) throw new InvalidTokenException();
        } catch (Exception e) {
            log.error("{}", e.getMessage());
            return;
        }
        Integer messageRes = jsonObject.getInteger("res");
        if (false) {

        }
        else {
            log.error("can not find message code!");
        }

    }

    @OnError
    public void onError(Session session, Throwable error) {
        if (!error.getClass().equals(ServiceException.class)) {
            log.error("", error);
        }
    }

    /**
     * 服务端发送消息给客户端
     */
    private void sendMessage(String message, Session toSession) {
        try {
            log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
            toSession.getBasicRemote().sendText(message);
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }

    /**
     * 服务端发送消息给所有客户端
     */
    private void sendAllMessage(String message) {
        try {
            for (Session session : UserSessionMap.getUserSessionMap().values()) {
                log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);
                session.getBasicRemote().sendText(message);
            }
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }
    /**
     * 服务端发送消息给所有客户端
     */
    private void sendAllMessageWithoutMe(String message, String me) {
        try {
            for (String openid : UserSessionMap.getUserSessionMap().keySet()) {
                if (!openid.equals(me)) {
                    Session session = UserSessionMap.getUserSessionMap().get(openid);
                    log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);
                    session.getBasicRemote().sendText(message);
                }
            }
        } catch (Exception e) {
            log.error("服务端发送消息给客户端失败", e);
        }
    }
//    @Transactional
    public void joinRoom(UserInfo userInfo, Session session) {
        UserSessionMap.addUserSession(userInfo.getOpenId(), session);
        RedisReactiveCommands<String, String> commands = RedisFactory.getRedisCommand();
//        commands.zadd(RedisSettingCode.USER_PREFIX.getKey() + )
    }
}
